Terraformのtemplatefile関数で使うファイルの中に別のファイルを埋め込むとインデントが崩れた
やりたいこと
以下のFluent Bitの設定の一部を切り出したファイルparsers.conf
があります。
[PARSER] Name crio Format regex Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) [PF] (?<log>.+)$ Time_Key time Time_Format %Y-%m-%dT%H:%M:%S.%L%z Time_Keep On
これを、AWS for fluent bitのvaluesを定義した以下YAMLファイルの中に埋め込みたいです。
image: tag: 2.25.0 serviceAccount: name: '${sa_name}' create: true annotations: eks.amazonaws.com/role-arn: ${sa_role_arn} input: parser: crio cloudWatch: enabled: true firehose: enabled: false kinesis: enabled: false elasticsearch: enabled: false
先程のparsers.conf
を使ってこういうキーを定義したいです。
service: extraParsers: |- [PARSER] Name crio Format regex Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) [PF] (?<log>.+)$ Time_Key time Time_Format %Y-%m-%dT%H:%M:%S.%L%z Time_Keep On
失敗1
templatefile関数のvarsに、file関数で読み込んだ文字列を渡してみました。
resource "helm_release" "ec2_node_logging" { name = "ec2-node-logging" namespace = kubernetes_namespace.aws_observability.id repository = "https://aws.github.io/eks-charts" chart = "aws-for-fluent-bit" version = "0.1.16" recreate_pods = true values = [ templatefile( "${path.module}/helm/override-values.yaml.tmpl", { sa_name = local.ec2_node_logging_service_account_name sa_role_arn = module.ec2_node_logging_sa_iam_role.this_iam_role_arn extra_parsers = file("${path.module}/conf/parsers.conf") } ) ] }
image: tag: 2.25.0 serviceAccount: name: '${sa_name}' create: true annotations: eks.amazonaws.com/role-arn: ${sa_role_arn} service: extraParsers: |- ${extra_parsers} input: parser: crio cloudWatch: enabled: true firehose: enabled: false kinesis: enabled: false elasticsearch: enabled: false
結果、この方法は上手くいきませんでした。
service.extraParsers
の値を元に/fluent-bit/etc/parser_extra.conf
という設定ファイルが作成されるのですが、インデントレベルエラーとなりました。
% kubectl logs ec2-node-logging-aws-for-fluent-bit-6mrzs (略) [2023/02/24 12:28:27] [error] [config] error in /fluent-bit/etc/parser_extra.conf:3: invalid indentation level (略)
Podの中に入ってファイルを確認してみたところ、2行目以降のインデントがなくなっていることがわかります。
% kubectl exec ec2-node-logging-aws-for-fluent-bit-6mrzs -- cat /fluent-bit/etc/parser_extra.conf [PARSER] Name crio Format regex Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) [PF] (?<log>.+)$ Time_Key time Time_Format %Y-%m-%dT%H:%M:%S.%L%z Time_Keep On%
helmコマンドでも確認。
% helm get values ec2-node-logging USER-SUPPLIED VALUES: (略) service: extraParsers: |- [PARSER] Name crio Format regex Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) [PF] (?<log>.+)$ Time_Key time Time_Format %Y-%m-%dT%H:%M:%S.%L%z Time_Keep On (略)
失敗2
テンプレートファイルの中でfile関数を使ってみましたが、結果は同じでした。
image: tag: 2.25.0 serviceAccount: name: '${sa_name}' create: true annotations: eks.amazonaws.com/role-arn: ${sa_role_arn} service: extraParsers: |- ${file("conf/parsers.conf")} input: parser: crio cloudWatch: enabled: true firehose: enabled: false kinesis: enabled: false elasticsearch: enabled: false
成功1
「失敗1」をベースに、indentとtrimspace関数を追加したところ成功しました。
resource "helm_release" "ec2_node_logging" { name = "ec2-node-logging" namespace = kubernetes_namespace.aws_observability.id repository = "https://aws.github.io/eks-charts" chart = "aws-for-fluent-bit" version = "0.1.16" recreate_pods = true values = [ templatefile( "${path.module}/helm/override-values.yaml.tmpl", { sa_name = local.ec2_node_logging_service_account_name sa_role_arn = module.ec2_node_logging_sa_iam_role.this_iam_role_arn extra_parsers = trimspace(indent(4, file("${path.module}/conf/parsers.conf"))) } ) ] }
「失敗1」の結果の通り、file関数で読み込んだ直後はインデントがなくなっています。
[PARSER] Name crio Format regex Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) [PF] (?<log>.+)$ Time_Key time Time_Format %Y-%m-%dT%H:%M:%S.%L%z Time_Keep On%
そこでindent関数で全行インデントします。
[PARSER] Name crio Format regex Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) [PF] (?<log>.+)$ Time_Key time Time_Format %Y-%m-%dT%H:%M:%S.%L%z Time_Keep On%
最後にtrimspace関数で最初の行だけ冒頭のスペースを削除しています。
[PARSER] Name crio Format regex Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) [PF] (?<log>.+)$ Time_Key time Time_Format %Y-%m-%dT%H:%M:%S.%L%z Time_Keep On%
ただこれは、今回のファイルの中身がこうだったから成功しただけなので、もっと複雑なインデントが必要だったら失敗しますね。
成功2
helm_release
のvalues attributeはlist型になっていて、複数のYAMLを指定できます。それら複数YAMLはマージされた状態でリリースに使われます。
List of values in raw yaml to pass to helm. Values will be merged, in order, as Helm does with multiple -f options. (引用元)
templatefile関数内で頑張るのをやめて、こちら(複数YAML定義する)でやってみたところうまく行きました。具体的には、Terraformのオブジェクトとして定義してから、yamlencode関数でYAML化したところ、インデントは保存されていました。
resource "helm_release" "ec2_node_logging" { name = "ec2-node-logging" namespace = kubernetes_namespace.aws_observability.id repository = "https://aws.github.io/eks-charts" chart = "aws-for-fluent-bit" version = "0.1.16" recreate_pods = true values = [ templatefile( "${path.module}/helm/override-values.yaml.tmpl", { sa_name = local.ec2_node_logging_service_account_name sa_role_arn = module.ec2_node_logging_sa_iam_role.this_iam_role_arn } ), yamlencode({ service = { extraParsers = file("${path.module}/conf/parsers.conf") } }) ] }
なぜTerraformのオブジェクトとして定義する場合はインデントが保存されるのかはわかりませんでした。が、以下の文を読むにYAMLやJSONを定義する場合は、Terraformオブジェクトとして定義してからencode関数をかますことを公式は推奨しているのではと思います。
Don't use "heredoc" strings to generate JSON or YAML. Instead, use the jsonencode function or the yamlencode function so that Terraform can be responsible for guaranteeing valid JSON or YAML syntax.